package com.kvn.interceptor;

import org.apache.ibatis.mapping.SqlSource;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created by wangzhiyuan on 2018/6/11
 */
public class SqlSourceProxyFactory implements InvocationHandler {
    private static final Map<String/* statementId$newSql */, Object/* 代理类 */> sqlSourceProxyCacheMap = new ConcurrentHashMap<>();

    private SqlSource sqlSource;
    private String newSql;
    /**
     * MappedStatement的id
     */
    private String statementId;

    private SqlSourceProxyFactory(SqlSource sqlSource, String newSql, String statementId) {
        this.sqlSource = sqlSource;
        this.newSql = newSql;
        this.statementId = statementId;
    }

    public Object createProxy() {
        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), sqlSource.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object boundSql = method.invoke(sqlSource, args);
        if (!"getBoundSql".equals(method.getName())) {
            return boundSql;
        }

        Field sqlField = boundSql.getClass().getDeclaredField("sql");
        sqlField.setAccessible(true);
        sqlField.set(boundSql, newSql);
        return boundSql;
    }


    public static Object getProxy(SqlSource sqlSource, String newSql, String statementId) {
        String key = statementId + "$" + newSql;
        Object proxy = sqlSourceProxyCacheMap.get(key);
        if (proxy == null) {
            synchronized (key) {
                proxy = sqlSourceProxyCacheMap.get(key);
                if (proxy == null) {
                    proxy = new SqlSourceProxyFactory(sqlSource, newSql, statementId).createProxy();
                    sqlSourceProxyCacheMap.put(key, proxy);
                }
            }
        }

        return sqlSourceProxyCacheMap.put(key, proxy);
    }
}
